import { Command } from 'commander';
import inquirer from 'inquirer';
import { readFileSync } from 'fs';
import { load as yamlLoad } from 'js-yaml';
import { api } from '../services/api';
import { config } from '../services/config';
import { logger } from '../utils/logger';
import { createTable } from '../utils/table';
import { requireAuth, requireProject } from '../utils/validation';
import { CLIError } from '../types';

export function secretCommands(program: Command): void {
  const secretCmd = program
    .command('secret')
    .description('Secret and configuration management');

  secretCmd
    .command('list')
    .description('List environment secrets')
    .option('-e, --env <env>', 'Target environment')
    .action(async (options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();

        const spinner = logger.spinner('Fetching secrets...');
        try {
          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          const secrets = await api.getSecrets(projectId, environment.id);
          spinner.succeed();

          if (secrets.length === 0) {
            logger.info(`No secrets configured for environment "${targetEnv}"`);
            return;
          }

          const headers = ['Key', 'Environment'];
          const rows = secrets.map(secret => [
            secret.key,
            secret.environment
          ]);

          logger.log(`\nSecrets in "${targetEnv}":`);
          logger.log(createTable(headers, rows));
          logger.info('\nValues are hidden for security. Use "cm secret get <key>" to view individual values.');
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to list secrets');
      }
    });

  secretCmd
    .command('set <keyValue>')
    .description('Set a secret (format: KEY=VALUE)')
    .option('-e, --env <env>', 'Target environment')
    .action(async (keyValue: string, options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();

        if (!keyValue.includes('=')) {
          throw new CLIError('Invalid format. Use: cm secret set KEY=VALUE');
        }

        const [key, ...valueParts] = keyValue.split('=');
        const value = valueParts.join('=');

        if (!key || !value) {
          throw new CLIError('Both key and value are required');
        }

        const spinner = logger.spinner(`Setting secret ${key}...`);
        try {
          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          await api.setSecret(projectId, environment.id, key, value);
          spinner.succeed();
          
          logger.success(`Secret "${key}" set successfully in "${targetEnv}"`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to set secret');
      }
    });

  secretCmd
    .command('delete <key>')
    .description('Delete a secret')
    .option('-e, --env <env>', 'Target environment')
    .action(async (key: string, options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();

        const confirm = await inquirer.prompt([
          {
            type: 'confirm',
            name: 'delete',
            message: `Delete secret "${key}" from environment "${targetEnv}"?`,
            default: false
          }
        ]);

        if (!confirm.delete) {
          logger.info('Operation cancelled');
          return;
        }

        const spinner = logger.spinner(`Deleting secret ${key}...`);
        try {
          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          await api.deleteSecret(projectId, environment.id, key);
          spinner.succeed();
          
          logger.success(`Secret "${key}" deleted successfully from "${targetEnv}"`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to delete secret');
      }
    });

  program
    .command('bind <file>')
    .description('Batch bind configuration from YAML file')
    .option('-e, --env <env>', 'Target environment')
    .action(async (filePath: string, options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();

        const spinner = logger.spinner('Reading configuration file...');
        try {
          const fileContent = readFileSync(filePath, 'utf8');
          const config_data = yamlLoad(fileContent) as Record<string, any>;
          
          if (!config_data || typeof config_data !== 'object') {
            throw new CLIError('Invalid YAML format');
          }

          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          spinner.text = 'Binding configuration...';

          const secrets = config_data.secrets || {};
          const modules = config_data.modules || [];

          let secretCount = 0;
          for (const [key, value] of Object.entries(secrets)) {
            await api.setSecret(projectId, environment.id, key, String(value));
            secretCount++;
          }

          let moduleCount = 0;
          for (const module of modules) {
            if (typeof module === 'string') {
              await api.installModule(projectId, environment.id, module);
              moduleCount++;
            } else if (module.name) {
              await api.installModule(projectId, environment.id, module.name, module.version);
              moduleCount++;
            }
          }

          spinner.succeed();
          
          logger.success(`Configuration bound successfully to "${targetEnv}"`);
          logger.info(`Secrets: ${secretCount} configured`);
          logger.info(`Modules: ${moduleCount} installed`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to bind configuration');
      }
    });
}